/**
 * Created by hdt3213 on 16-10-30.
 */

var Score = {};
Score.INF = 800000;
Score.L4L4 = 750000; // living 4, living 4
Score.L4D4 = 700000; // living 4, dead 4
Score.L4L3 = 650000;
Score.D4D4 = 600000;
Score.SL4 = 550000; // single living 4
Score.SD4 = 500000;
Score.JL4L3 = 500000; // jump living 4
Score.D4L3 = 450000;
Score.JD4L3 = 400000;
Score.L3L3 = 350000;

var Formation = {};
Formation.SAFE = 0;
Formation.L4L4 = 1;
Formation.L4D4 = 2;
Formation.L4L3 = 3;
Formation.D4D4 = 4;
Formation.SL4 = 5;
Formation.SD4 = 6;
Formation.FIVE = 7;
Formation.D4L3 = 8;
Formation.JL4L3 = 9;
Formation.JD4L3 = 10;
Formation.L3L3 = 11;
Formation.SL3 = 12; // todo: defend SL3

/*
    Linear:{role:int, stone:int, jump:int, leftBlock:bool, rightBlock:bool, score:float}
    Condition: {formation, score}
    Step {x:int, y:int, role, score}
    Border {east, west, north, south}
 */

var autoplayer = Object();
autoplayer.role = VACANT;

var blockDecent = 2, jumpBase = 8, scoreBase = 8;
var defend = 5, attack = 3, rash = 1, maxDepth = 8;

function sortLinear(conditions) {
    // descending sort linear conditions according to  score
	conditions.sort(function(x,y) {
		return y.score - x.score;
	});
    return conditions
}

function nearestPos(steps) {
    // get the most nearest element of the center
    var t = -1, min = Score.INF, dis, arr = []; // arr[Len][Len]
    // find min distant
    for (var i = 0; i < steps.length; i++) {
        dis = Math.pow(steps[i].x - 7, 2) + Math.pow(steps[i].y - 7, 2);
        if (dis < min) {
            min = dis;
            t = i;
        }
    }
    arr.push(t);
    // find equal distant pos
    for (i = 0; i < steps.length; i++) {
        dis = Math.pow(steps[i].x - 7, 2) + Math.pow(steps[i].y - 7, 2);
        if (dis < min) {
            arr.push(i);
        }
    }
    i = Math.floor(Math.random() * arr.length); // get rand index
    return arr[i];
}

function changeBorder(border, x, y) {
    border.west  = (x < border.west)  ? x : border.west;
	border.east  = (x > border.east)  ? x : border.east;
	border.north = (y < border.north) ? y : border.north;
	border.south = (y > border.south) ? y : border.south;
}


// evaluate condition on a single line
function evaluateLinearCondition(step, off_x, off_y) {
    var i, num, x, y, t_x, t_y, stone, jump;  // int
    var leftBlock, rightBlock, abort;  // bool
    var score, partner_step = {}, linear = {}; // score:float, partner_step:Step, condition: LinearCondition
    leftBlock = false;
    rightBlock = false;
    stone = 1;
    jump = 0;
    num = 0;
    partner_step.role = revert_role(step.role);
    for (i = 1; i< 5; i++) {
        abort = true;
        num++;
        x = step.x - i * off_x;
        y = step.y - i * off_y;
        if (x < 0 || y < 0 || x >= LEN || y >= LEN) {
            leftBlock = true;
            break;
        }
        if (model.getPos(x, y) == step.role) {
            stone++;
        }
        else if (model.getPos(x,y) == revert_role(step.role)) {
            leftBlock = true;
            break;
        }
        else if (model.getPos(x, y) == VACANT) {
            // check jump
            partner_step.x = x;
            partner_step.y = y;
            t_x = x - off_x;
            t_y = y - off_y;
            if (t_x < 0 || t_y < 0 || x >= LEN || y >= LEN) {
                leftBlock = false;
                break;
            }
            if (model.getPos(t_x, t_y) == VACANT) {
                leftBlock = false;
                break;
            }
            if (model.getPos(t_x, t_y) == step.role) {
				jump++;
			}
			else if (model.getPos(t_x, t_y) == revert_role(step.role)) {
				leftBlock = false;
				break;
			}
        }
        abort = false;
    }
    //If exit the loop normally, we need to judge the left stone
	if (abort == false) {
		t_x = x - off_x;
		t_y = y - off_y;
		if (t_x < 0 || t_y < 0 || x >= LEN || y >= LEN) {
			leftBlock = true;
		}
		else  {
			if (model.getPos(t_x, t_y) == VACANT) {
				leftBlock = false;
			}
			else if (model.getPos(t_x, t_y) == revert_role(step.role)) {
				leftBlock = true;
			}
		}
	}
	num--;

    // check opposite direction
	for (i = 1; i <= 4 - num; i++) {  //warning : unknown index limitation
		abort = true;
		x = step.x + i * off_x;
		y = step.y + i * off_y;
		if (x < 0 || y < 0 || x >= LEN || y >= LEN) {
			rightBlock = true;
			break;
		}
		if (model.getPos(x, y) == step.role) {
			stone++;
		}
		else if (model.getPos(x, y) == revert_role(step.role)) {
			rightBlock = true;
			break;
		}
		else if (model.getPos(x, y) == VACANT) {
			partner_step.x = x;
			partner_step.y = y;
			t_x = x + off_x;
			t_y = y + off_y;
			
			if (t_x < 0 || t_y < 0 || x >= LEN || y >= LEN) {
				rightBlock = false;
				break;
			}
			if (model.getPos(t_x, t_y) == VACANT) {
				rightBlock = false;
				break;
			}
			if (model.getPos(t_x, t_y) == step.role) {
				jump++;
			}
			else if (model.getPos(t_x, t_y) == revert_role(step.role)) {
				rightBlock = false;
				break;
			}
		}
		abort = false;
	}
	if (abort == false) {
		t_x = x - off_x;
		t_y = y - off_y;
		if (t_x < 0 || t_y < 0 || x >= LEN || y >= LEN) {
			rightBlock = true;
		}
		else  {
			if (model.getPos(t_x, t_y) == VACANT) {
				rightBlock = false;
			}
			else if (model.getPos(t_x, t_y) == revert_role(step.role)) {
				rightBlock = true;
			}
		}
	}

	//evaluate the Score
	score = Math.pow(scoreBase, stone);
	if (jump != 0) {
		score -= Math.pow(jumpBase, jump);
	}
	if (leftBlock || rightBlock) {
		score /= blockDecent;
	}
	if (leftBlock && rightBlock) {
		score = 1;
	}

	if (stone == 5) {
		score = Score.INF;
	}

	linear.role = step.role;
	linear.leftBlock  = leftBlock;
	linear.rightBlock = rightBlock;
	linear.score = score;
	linear.stone = stone;
	linear.jump = jump;
	return linear;
}


// evaluate condition of the lines' crossing point
function evaluateJointCondition(step) {
    var cond = {}; // {formation, score}
    var linears = [], linear1 = {}, linear2 = {}; // linear condition
    linears.push(evaluateLinearCondition(step, 1, 0));
    linears.push(evaluateLinearCondition(step, 0, 1));
    linears.push(evaluateLinearCondition(step, 1, 1));
    linears.push(evaluateLinearCondition(step, -1, 1));
    sortLinear(linears);
	linear1 = linears[0];
	linear2 = linears[1];
    //Judge joint condition

    //L4L4, L4D4, D4D4
    if (linear1.stone == 4 && linear2.stone == 4) {
        if (!linear1.leftBlock && !linear1.rightBlock && !linear2.leftBlock && !linear2.rightBlock) {
            cond.formation = Formation.L4L4;
            cond.score = Score.L4L4;
        }
        if (!linear1.leftBlock && !linear1.rightBlock && (linear2.leftBlock || linear2.rightBlock)) {
            // After sorting, linear1 is more dangerous.
            // The condition that linear2 is L4 && linear1 is not L4, is impossible.
            cond.formation = Formation.L4D4;
            cond.score = Score.L4D4;
            return cond;
        }
        if (linear1.leftBlock && linear1.rightBlock && linear2.leftBlock && linear2.rightBlock) {
            cond.formation = Formation.D4D4;
            cond.score = Score.D4D4s;
            return cond;
        }
    }

    //L4L3
	if (linear1.stone == 4 && linear1.jump == 0 && !linear1.leftBlock && !linear1.rightBlock ) {
		if (linear2.stone == 3 && linear2.jump < 2 && !linear2.leftBlock&& !linear2.rightBlock) {
			cond.formation = Formation.L4L3;
			cond.score = Score.L4L3;
			return cond;
		}
	}

    //SL4
	if (linear1.stone == 4 && linear1.jump == 0 && !linear1.leftBlock && !linear1.rightBlock) {
		cond.formation = Formation.SL4;
		cond.score = Score.SL4;
		return cond;
	}

    //SD4
    if (linear1.stone == 4 && linear1.jump == 0 && (linear1.leftBlock && !linear1.rightBlock) && (!linear1.leftBlock && linear1.rightBlock) ) {
		cond.formation = Formation.SD4;
		cond.score = Score.SD4;
		return cond;
	}

    //D4L3
	if (linear1.stone == 4 && linear1.jump == 0 && linear2.stone == 3 && linear2.jump < 2) {
		if (!linear2.leftBlock && !linear2.rightBlock) {
			cond.formation = Formation.D4L3;
			cond.score = Score.D4L3;
			return cond;
		}
	}

    //L3L3
	if (linear1.stone == 3 && linear1.jump < 2 && !linear1.leftBlock && !linear1.rightBlock) {
		if (linear2.stone == 3 && linear2.jump < 2 && !linear2.leftBlock && !linear2.rightBlock) {
			cond.formation = Formation.L3L3;
			cond.score = Score.L3L3;
			return cond;
		}
	}

    //JL4L3
	if (linear1.stone == 4 && linear1.jump != 0 && !linear1.leftBlock && !linear1.rightBlock) {
		if (linear2.stone == 3 && linear2.jump < 2 && !linear2.leftBlock && !linear2.rightBlock) {
			cond.formation = Formation.JL4L3;
			cond.score = Score.JL4L3;
			return cond;
		}
	}

    //JD4L3
	if (linear1.stone == 4 && linear1.jump != 0 && linear2.stone == 3 && linear2.jump < 2) {
		if (linear1.leftBlock || linear1.rightBlock) {
			cond.formation = Formation.JD4L3;
			cond.score = Score.JD4L3;
			return cond;
		}
	}

	//SL3
	if (linear1.stone == 3 && linear1.jump == 0 && !linear1.leftBlock && !linear1.rightBlock) {
		cond.formation = Formation.SL3;
		cond.score = Score.SL3;
		return cond;
	}

    //Others
	cond.Formation = Formation.SAFE;
	cond.score = linear1.score + linear2.score;
	return cond;
}

function evaluateFormation(border, role, formations) {
	var i, j;
	var item = {}, step = {}, condition={}, formation={};
	for (i = border.west; i <= border.east; i++) {
		for (j = border.north; j <= border.south; j++) {
			if (model.getPos(i, j) != VACANT) {
				continue;
			}
			step.x = i;
			step.y = j;
			step.role = role;
			condition = evaluateJointCondition(step);
			formation = condition.formation;
			for (item in formations) {
				if (formation == item) {
					step.score = condition.score;
					return step;
				}
			}
		}
	}
	return null;
}

/*
	option priority:
	* offensive: fatal
	* defensive: fatal
	* offensive: L4L4, L4D4, D4D4, L4L3, D4L3
	* offensive: SL4
	* defensive: L4L4, L4D4, D4D4, L4L3, D4L3
	* defensive: SL4, SD4
	* offensive: JL4L3, JD4L3
	* offensive: L3L3
	* defensive: JL4L3, JD4L3
	* defensive: L3L3
 */

function evaluateEssentialStep(border, role) {
    var i, j, hostile;
    var step = {}, formation = {}, condition = {};
	var formations = [], options = [];
    // Fatal to partner
	for (i = border.west; i <= border.east; i++) {
		for (j = border.north; j <= border.south; j++) {
			if (model.getPos(i, j) != VACANT) {
				continue;
			}
			step.x = i;
			step.y = j;
			step.role = role;
			if (judge(step) == autoplayer.role) {
				step.score = Score.INF;
				return step;
			}
		}
	}

    // Fatal to autoplayer
	for (i = border.west; i <= border.east; i++) {
		for (j = border.north; j <= border.south; j++) {
			if (model.getPos(i, j) != VACANT) {
				continue;
			}
			step.x = i;
			step.y = j;
			step.role = revert_role(role);
			if (judge(step) == revert_role(role)) { // todo: check
				step.role = role;
				step.score = Score.INF;
				return step;
			}
		}
	}

	hostile = revert_role(role);
	options.push([[Formation.L4L4, Formation.L4D4, Formation.D4D4, Formation.L4L3, Formation.D4L3], role]);
	options.push([[Formation.SL4], role]);
	options.push([[Formation.L4L4, Formation.L4D4, Formation.D4D4, Formation.L4L3, Formation.D4L3], hostile]);
	options.push([[Formation.SL4, Formation.SD4], hostile]);
	options.push([[Formation.JL4L3, Formation.JD4L3], role]);
	options.push([[Formation.L3L3], role]);
	options.push([[Formation.JL4L3, Formation.JD4L3], hostile]);
	options.push([[Formation.L3L3], hostile]);

	for (var option in options) {
		formations = option[0];
		role = option[1];
		step = evaluateFormation(border, role, formations);
		if (step) {
			return step;
		}
	}

	step = {};
    step.score = 0;
    return step;
}

function getBestPos(border, role) {
	// search essential steps, if not exist, evaluate other situation
    var i, j, size, next; // int
    var score, autoBest = -Score.INF, manBest = -Score.INF; // float
    var step = {}, result = {}; // Step
    var autoSteps = [], autoBestSteps = [], manSteps = [], manBestSteps = [], compreBestSteps = []; //  comprehensive best steps
    var jointCondition;

	// if essential step exists
    step = evaluateEssentialStep(border, role);
    if (step.score != 0) {
        return step;
    }

	// other situation
	for (i = border.west; i <= border.east; i++) {
		for (j = border.north; j <= border.south; j++) {
			if (model.getPos(i, j) != VACANT) {
				continue;
			}
			step.x = i;
			step.y = j;
			step.role = role;
			jointCondition = evaluateJointCondition(step);
			score = jointCondition.score;
			step.score = score;
			if (score > autoBest) {
				autoBest = score;
			}
			autoSteps.push($.extend({}, step));

			step.role = revert_role(role);
			jointCondition = evaluateJointCondition(step);
			score = jointCondition.score;
			step.score = score;
			if (score > manBest) {
				manBest = score;
			}
			manSteps.push($.extend({}, step)); // deep copy

		}
	}
	for (i = 0; i < autoSteps.length; i++) {
		if (autoSteps[i].score == autoBest || autoSteps[i].score > Score.INF - 500) {
			autoBestSteps.push($.extend({}, autoSteps[i])); // deep copy
		}
	}
	for (i = 0; i < manSteps.length; i++) {
		if (manSteps[i].score == manBest || manSteps[i].score > Score.INF - 500) {
			manBestSteps.push($.extend({}, autoSteps[i])); // deep copy
		}
	}
	// evaluate comprehensive condition
	for (i = 0; i < autoBestSteps.length; i++) {
		for (j = 0; j < manBestSteps.length; j++) {
			if (autoBestSteps[i].x == manBestSteps[j].x && autoBestSteps[i].y == manBestSteps[j].y) {
				step = autoBestSteps[i];
				step.score = (autoBestSteps[i].score + manBestSteps[j].score) / 2;
				compreBestSteps.push($.extend({}, step)); // deep copy
			}
		}
	}

	// Search the best pos
	size = compreBestSteps.length;
	if (size >= 1) {
		next = nearestPos(compreBestSteps, size);
		result = compreBestSteps[next];
		result.role = role;
		return result;
	}
	//Our side is about to win
	if (autoBest > 36000) {
		size = autoBestSteps.length;
		next = Math.floor(Math.random() * size);
		result = autoBestSteps[next];
		result.role = role;
		return result;
	}

	// man player is about to win
	if (manBest > 36000) {
		size = manBestSteps.length;
		next = Math.floor(Math.random() * size);
		result = manBestSteps[next];
		result.role = role;
		return result;
	}

	//White
	if (role == WHITE) {
		if (autoBest * attack > manBest) {
			size = autoBestSteps.length;
			next = nearestPos(autoBestSteps, size);
			result = autoBestSteps[next];
			return result;
		}
		else {
			size = manBestSteps.length;
			next = nearestPos(manBestSteps, size);
			result = manBestSteps[next];
			return result;
		}
	}
	else if (role == BLACK) {
		if (autoBest > manBest * attack) {
			size = autoBestSteps.length;
			next = nearestPos(autoBestSteps, size);
			result = autoBestSteps[next];
			return result;
		}
		else {
			size = manBestSteps.length;
			next = nearestPos(manBestSteps, size);
			result = manBestSteps[next];
			return result;
		}
	}
	result.role = role;
	return result;
}

function react() {
	var step = {}, border = {};
	border.west = 0;
	border.east = 14;
	border.north = 0;
	border.south = 14;
	step = getBestPos(border, autoplayer.role);
	return step;
}

function randomReact() {
    var x, y, step = {};
    while (true) {
        x = Math.floor(Math.random() * LEN);
        y = Math.floor(Math.random() * LEN);
        if (model.getPos(x, y) == VACANT) {
            step.x = x;
            step.y = y;
            step.role = autoplayer.role;
            return step;
        }
    }

}